/**
 * 
 */
package poc.android.json;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import poc.android.MainLauncherActivity;
import poc.android.model.Role;
import poc.android.model.School;
import poc.android.model.User;
import poc.android.weather.R;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

/**
 * @author Jason
 *
 */
public class JSonMainActivity extends Activity {

	private static final String TAG="[JSonMainActivity]";
	
	EditText url;
	EditText result;
	Button btnGet;
	Button btnJSonCreate;
	Button btnJSonCreateBack;
	
	/**
	 * {@inheritDoc}
	 * @param savedInstanceState
	 */
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		this.setContentView(R.layout.json_main_activity);
		
		this.setupWidgets(savedInstanceState);
	}

	/**
	 * 
	 * @param pBundle
	 */
	private void setupWidgets(Bundle pBundle) {
		this.url=(EditText) this.findViewById(R.id.jsonMainUrl);
		// might use a spinner instead...
		this.url.setText("http://twitter.com/statuses/user_timeline/vogella.json");
		this.result=(EditText) this.findViewById(R.id.jsonMainResult);
		this.btnGet=(Button) this.findViewById(R.id.jsonMainGet);
		this.btnGet.setOnClickListener(new Button.OnClickListener() {
			//@Override
			public void onClick(View v) {
				String aUrl=url.getText().toString();
				String aResult=null;
				
				if (aUrl!=null && aUrl.trim().length()>0) {
					// call the handler...
					aResult=onGetClick(aUrl);
					// JSON Array handling
					jsonToObject(aResult);
					result.setText(aResult);
				}
			}
		});
		this.btnJSonCreate=(Button) this.findViewById(R.id.jsonCreate);
		this.btnJSonCreate.setOnClickListener(new Button.OnClickListener() {
			//@Override
			public void onClick(View arg0) {
				String aResult=null;
				
				aResult=createJSonObject();
				result.setText(aResult);
			}
		});
		this.btnJSonCreateBack=(Button) this.findViewById(R.id.jsonCreateBack);
		this.btnJSonCreateBack.setOnClickListener(new Button.OnClickListener() {
			//@Override
			public void onClick(View v) {
				String aResult=null;
				
				aResult=createJSonBack();
				result.setText(aResult);
			}
		});
	}
	
	/**
	 * method to demostrate how to make use of the static methods in a 
	 * AbstractJSonModel to transform a JSonString or JSONObject back to the 
	 * targeted object (eg. in this case, the "User")
	 * @return
	 */
	private String createJSonBack() {
		StringBuilder aSB=new StringBuilder(200);
		String aJSonString=this.createJSonObject();
		
		/*
		 * turn it back... by using the 2 static method(s)
		 */
		if (aJSonString!=null && aJSonString.trim().length()>0) {
			try {
				JSONArray aJArr=new JSONArray(aJSonString);
				for (int i=0; i<aJArr.length(); i++) {
					User aUser=(User) User.toObjectFromJSonObject(aJArr.getJSONObject(i));
					aSB.append("User retrieved: "+aUser+" rolesInSchool: "+aUser.getRolesInSchool()).append("\r\n");
				}
			} catch (Exception e) {
				e.printStackTrace();
				throw new RuntimeException("[][] trouble in transforming a JSonString back to User(s)", e);
			}
		}
		return aSB.toString();
	}
	
	/**
	 * simple demo on creation of JSONObject from normal POJO
	 * @return
	 */
	private String createJSonObject() {
		StringBuilder aSB=new StringBuilder(200);
		JSONArray aJArr=new JSONArray();
		User aStudent01=new User();
		User aTeacher01=new User();
		List<Role> aRoleList=new ArrayList<Role>();
		
		aStudent01.setId(Integer.valueOf(101));
		aStudent01.setName("Jacky Lee");
		aStudent01.setSchool(School.availableSchools[1]);
		aRoleList.clear();
		aRoleList.add(Role.availableRoles[0]);
		aStudent01.setRolesInSchool(aRoleList);
		
		aTeacher01.setId(Integer.valueOf(201));
		aTeacher01.setName("Johnathan");
		aTeacher01.setSchool(School.availableSchools[0]);
		aRoleList=new ArrayList<Role>();
		aRoleList.add(Role.availableRoles[1]);
		aRoleList.add(Role.availableRoles[2]);
		aTeacher01.setRolesInSchool(aRoleList);
		/*
		 *  json marshal
		 */
		try {
			aJArr.put(0, aStudent01.toJSon(null));
			aJArr.put(1, aTeacher01.toJSon(null));
			
			aSB.append(aJArr.toString());
			
		} catch (JSONException e) {
			e.printStackTrace();
			Log.e(TAG, "[JSonMainActivity][createJSonObject] failed to set the JSONObject to the JSONArray", e);
		}
		return aSB.toString();
	}
	
	/**
	 * simple method to demo how to handle the returned JSon String; 
	 * simply it is not a marshaller... you still need to unmarshall it
	 * according to the returned String
	 * @param pJSon
	 */
	private void jsonToObject(final String pJSon) {
		if (pJSon!=null && pJSon.trim().length()>0) {
			try {
				JSONArray aJArr=new JSONArray(pJSon);
				/*
				 * 0) there is no... method to check whether the object/field 
				 * 	is really an Array or not... so either ...
				 * 
				 * 	a) define the Object hierarchy carefully 
				 * 		(for 1 to many fields, use List even if only 
				 * 		1 item exist)
				 * 	b) have APIs written to handle these exceptional cases 
				 * 		{ps. not a good approach in general, too many cases to 
				 * 		cater for in the future}
				 */
				for (int i=0; i<aJArr.length(); i++) {
					JSONObject aJObj=aJArr.getJSONObject(i);
					/*
					 *	1) if the field is not array... and u try to get it in the form of JSONArray..
					 *		exception will be thrown 
					 */
					//JSONArray aIJArr=aJObj.getJSONArray("text");
					//Log.i(TAG, "[jsonToObject] "+ aIJArr.length() +" size of 'text' array; note that 'text' is not an Array in original, we just try to test whether the JSon API can handle such problems~ contents if length>0 "+(aIJArr.length()>0?aIJArr.getString(0):"NOPE, no data"));
					/*
					 * 2) it's simple and easy to getString('field') from the JSONObject
					 */
					Log.i(TAG, "[jsonToObject] in_reply_to_screen_name: "+ aJObj.getString("in_reply_to_screen_name") +" text: "+aJObj.getString("text"));
					//Log.i(TAG, "[jsonToObject] index "+i+" "+aJObj);
				}
				
			} catch (JSONException e) {
				e.printStackTrace();
				throw new RuntimeException("[JSonMainActivity][jsonToObject] can't unmarshall the JSon String : "+pJSon, e);
			}
		}
	}
	
	/**
	 * <b>PS</b>. event handlers should not have UI dependant code... so it could be
	 * reused somewhere else...
	 * @param pUrl
	 */
	private String onGetClick(String pUrl) {
		URL aUrl=null;
		URLConnection aConnection=null;
		BufferedReader aBReader=null;
		StringBuilder aSB=new StringBuilder(500);
		
		try {
			String aLine=null;
			
			aUrl=new URL(pUrl);
			aConnection=aUrl.openConnection();
			aBReader=new BufferedReader(new InputStreamReader(aConnection.getInputStream()));
			
			while ((aLine=aBReader.readLine())!=null) {
				if (aSB.length()>0) aSB.append("\r\n");
				aSB.append(aLine);
			}
			
		} catch (Exception e) {
			throw new RuntimeException("[JSonMainActivity][onGetClick] can't get the URL unexpectedly~ ", e);
			
		} finally {
			if (aBReader!=null) try { aBReader.close(); } catch (Exception e) {}
			if (aUrl!=null) aUrl=null;
			if (aConnection!=null) aConnection=null;
		}
		return aSB.toString();
	}
	
	/**
	 * {@inheritDoc}
	 */
	@Override
	protected void onPause() {
		super.onPause();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected void onResume() {
		super.onResume();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		boolean aOk=false;
		
		if (super.onCreateOptionsMenu(menu)) {
			MenuInflater aInflater=this.getMenuInflater();
			try {
				aInflater.inflate(R.menu.json_main_menu, menu);	
				aOk=true;
				
			} catch (Exception e) {
				e.printStackTrace();
				Log.e(TAG, "[JSonMainActivity][onCreateOptionsMenu]", e);
			}
		}
		return aOk;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		boolean aOk=false;
		
		switch(item.getItemId()) {
		case R.id.jsonHome:
			this.backHomeIntent();
			aOk=true;
			break;
		default:
			aOk=super.onOptionsItemSelected(item);
		}
		return aOk;
	}
	
	/**
	 * back to home... page
	 */
	private void backHomeIntent() {
		Intent aIntent=new Intent(MainLauncherActivity.INTENT_HOME);
		this.startActivity(aIntent);
		this.finish();
	}
}
